{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "## Callisto tket framework\n", "\n", "The tket framework is a quantum software framework primarily used for the development and execution of quantum algorithms (quantum circuits). It is designed to be platform-agnostic software, and there are a lot of extensions available which allow tket to interface with backends from a range of providers ( see tket extension ).\n", "\n", "Due to its popularity and the broad amount of available backends using this framework, we wrote an extension to tket that enables a user to communicate with the C12 emulator Callisto . The backend name is `CallistoBackend`.\n", "\n", "We need to create a circuit using the `Circuit` class to run a quantum algorithm." ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[X q[0];, CX q[0], q[1];]\n" ] } ], "source": [ "from pytket import Circuit\n", "\n", "circuit = Circuit(2)\n", "circuit.X(0)\n", "circuit.CX(0, 1)\n", "\n", "print(circuit.get_commands())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Internally all circuits in the pytket framework are represented as directed acyclic graphs (DAG). Using ptyket util package it is possible to see the corresponding DAG of the chosen circuit." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A classical model for quantum computing is a model where the main program is run on the host computer, which occasionally sends off jobs to the quantum computer and retrieves the results. A backend represents a connection to some instance, either quantum hardware or an emulator. It presents a uniform interface for submitting circuits to be processed and retrieving the results. The main goal of this approach is to promote the development of platform-independent software, helping the code that the developers write to be more future-proof.\n", "\n", "Using tket's abstract class, `Backend,` it is possible to set different characteristics of a device that it should represent. The main properties that a backend need to implement are:\n", "\n", "1. The restrictions that are specific to specific quantum devices. Different constraints are encoded using the predicates, which are essentially a boolean property of a circuit that must return True to run the circuit on the target machine.\n", "\n", "2. Sending a circuit to the target device and examining the results. This process can be accomplished by calling `Backend.process_circuit()` or `Backend.process_circuits()` methods. These methods will send a circuit for execution and return an instance of `ResultHandle` as a result of their execution. The `ResultHandle` is a unique identifier that can be used to retrieve the actual results once the job has finished.\n", "\n", "3. Retrieval of the results. Obtaining the results of a successfully run job\n", " is done using the `Backend.get_result()` method, which returns an instance of `BackendResult` class. The class `BackendResult` has methods that can be useful for obtaining different pieces of information about the job that has been run. These methods are:\n", "`get_state()` - get the state vector\n", "`get_shots()` - get the shots. The shots are returned as a 2D array of the result for each shot.\n", "`get_counts()` - get the counts (it is obtained from the shots directly)\n", "`get_density_matrix()` - get the density matrix of the job.\n", "\n", "\n", "To implement the C12 emulator Callisto, we have developed `CallistoBackend` class with all the above options incorporated." ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "('ffb8e2ff-307a-4f02-8063-13b950a8dafe',)\n" ] } ], "source": [ "import os\n", "from c12_callisto_clients.pytket.extensions.callisto import CallistoBackend\n", "\n", "access_token = os.getenv(\"C12_TOKEN\") # Token that is obtained for allowing the access to the system\n", "backend_name = \"c12sim-iswap\"\n", "\n", "# create callisto backend instance\n", "callisto = CallistoBackend(backend_name, token=access_token, verbose=False)\n", "\n", "# Get the ResultHandle instance\n", "handle = callisto.process_circuit(circuit, n_shots=1024)\n", "print(handle)" ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "CircuitStatus(status=, message='', error_detail=None, completed_time=None, queued_time=None, submitted_time=None, running_time=None, cancelled_time=None, error_time=None, queue_position=None)\n", "[[0 0]\n", " [1 0]\n", " [0 1]\n", " ...\n", " [1 1]\n", " [1 1]\n", " [1 1]]\n", "Counter({(1, 1): 1021, (0, 0): 1, (0, 1): 1, (1, 0): 1})\n" ] } ], "source": [ "job_uuid = handle[0]\n", "\n", "# Get BackendResult instance\n", "job_result = callisto.get_result(handle)\n", "\n", "\n", "print(callisto.circuit_status(handle)) # Get the status of the circuit\n", "print(job_result.get_shots())\n", "print(job_result.get_counts())" ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "collapsed": false }, "outputs": [ { "ename": "CircuitNotValidError", "evalue": "Circuit with index 0 in submitted does not satisfy MaxNQubitsPredicate(13) (try compiling with backend.get_compiled_circuits first).", "output_type": "error", "traceback": [ "\u001B[0;31m---------------------------------------------------------------------------\u001B[0m", "\u001B[0;31mCircuitNotValidError\u001B[0m Traceback (most recent call last)", "\u001B[0;32m/var/folders/kf/wdsyjzyj4vzf0cclsfx3swgh0000gn/T/ipykernel_39837/1469770625.py\u001B[0m in \u001B[0;36m\u001B[0;34m\u001B[0m\n\u001B[1;32m 4\u001B[0m \u001B[0mcircuit2\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0mCX\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0;36m0\u001B[0m\u001B[0;34m,\u001B[0m \u001B[0mn\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m 5\u001B[0m \u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0;32m----> 6\u001B[0;31m \u001B[0mhandle2\u001B[0m \u001B[0;34m=\u001B[0m \u001B[0mcallisto\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0mprocess_circuit\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0mcircuit2\u001B[0m\u001B[0;34m,\u001B[0m \u001B[0mn_shots\u001B[0m\u001B[0;34m=\u001B[0m\u001B[0;36m1024\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0m\u001B[1;32m 7\u001B[0m \u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m 8\u001B[0m \u001B[0;31m# This should fail due to MaxNQubitsPredicate which controls the number of qubits that can be run on the emulator\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n", "\u001B[0;32m/opt/anaconda3/lib/python3.9/site-packages/c12_callisto_clients/pytket/extensions/callisto/backends/callisto.py\u001B[0m in \u001B[0;36mprocess_circuit\u001B[0;34m(self, circuit, n_shots, valid_check, **kwargs)\u001B[0m\n\u001B[1;32m 313\u001B[0m \"\"\"\n\u001B[1;32m 314\u001B[0m \u001B[0;32mif\u001B[0m \u001B[0mvalid_check\u001B[0m\u001B[0;34m:\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0;32m--> 315\u001B[0;31m \u001B[0mself\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0m_check_all_circuits\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0;34m[\u001B[0m\u001B[0mcircuit\u001B[0m\u001B[0;34m]\u001B[0m\u001B[0;34m)\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0m\u001B[1;32m 316\u001B[0m \u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m 317\u001B[0m \u001B[0mn_shots\u001B[0m \u001B[0;34m=\u001B[0m \u001B[0;36m1024\u001B[0m \u001B[0;32mif\u001B[0m \u001B[0mn_shots\u001B[0m \u001B[0;32mis\u001B[0m \u001B[0;32mNone\u001B[0m \u001B[0;32melse\u001B[0m \u001B[0mn_shots\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n", "\u001B[0;32m/opt/anaconda3/lib/python3.9/site-packages/pytket/backends/backend.py\u001B[0m in \u001B[0;36m_check_all_circuits\u001B[0;34m(self, circuits, nomeasure_warn)\u001B[0m\n\u001B[1;32m 123\u001B[0m )\n\u001B[1;32m 124\u001B[0m \u001B[0;32mfor\u001B[0m \u001B[0merror\u001B[0m \u001B[0;32min\u001B[0m \u001B[0merrors\u001B[0m\u001B[0;34m:\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0;32m--> 125\u001B[0;31m \u001B[0;32mraise\u001B[0m \u001B[0merror\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[0m\u001B[1;32m 126\u001B[0m \u001B[0;32mif\u001B[0m \u001B[0mnomeasure_warn\u001B[0m\u001B[0;34m:\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n\u001B[1;32m 127\u001B[0m \u001B[0;32mif\u001B[0m \u001B[0mcirc\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0mn_gates_of_type\u001B[0m\u001B[0;34m(\u001B[0m\u001B[0mOpType\u001B[0m\u001B[0;34m.\u001B[0m\u001B[0mMeasure\u001B[0m\u001B[0;34m)\u001B[0m \u001B[0;34m<\u001B[0m \u001B[0;36m1\u001B[0m\u001B[0;34m:\u001B[0m\u001B[0;34m\u001B[0m\u001B[0;34m\u001B[0m\u001B[0m\n", "\u001B[0;31mCircuitNotValidError\u001B[0m: Circuit with index 0 in submitted does not satisfy MaxNQubitsPredicate(13) (try compiling with backend.get_compiled_circuits first)." ] } ], "source": [ "circuit2 = Circuit(20)\n", "for n in range(1, 20):\n", " circuit2.X(n)\n", " circuit2.CX(0, n)\n", "\n", "handle2 = callisto.process_circuit(circuit2, n_shots=1024)\n", "\n", "# This should fail due to MaxNQubitsPredicate which controls the number of qubits that can be run on the emulator" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.9.13" } }, "nbformat": 4, "nbformat_minor": 4 }